home *** CD-ROM | disk | FTP | other *** search
/ OpenGL Superbible (2nd Edition) / OpenGL SuperBible e2.iso / tools / FLTK-1.0.6 / src / fl_file_chooser.cxx < prev    next >
Encoding:
C/C++ Source or Header  |  1999-01-07  |  17.8 KB  |  627 lines

  1. //
  2. // "$Id: fl_file_chooser.cxx,v 1.10 1999/01/07 21:22:28 mike Exp $"
  3. //
  4. // File chooser widget for the Fast Light Tool Kit (FLTK).
  5. //
  6. // Copyright 1998-1999 by Bill Spitzak and others.
  7. //
  8. // This library is free software; you can redistribute it and/or
  9. // modify it under the terms of the GNU Library General Public
  10. // License as published by the Free Software Foundation; either
  11. // version 2 of the License, or (at your option) any later version.
  12. //
  13. // This library is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16. // Library General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Library General Public
  19. // License along with this library; if not, write to the Free Software
  20. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  21. // USA.
  22. //
  23. // Please report all bugs and problems to "fltk-bugs@easysw.com".
  24. //
  25.  
  26. #include <config.h>
  27. #include <FL/fl_file_chooser.H>
  28.  
  29. #include <FL/Fl.H>
  30. #include <FL/Fl_Window.H>
  31. #include <FL/Fl_Box.H>
  32. #include <FL/Fl_Button.H>
  33. #include <FL/Fl_Return_Button.H>
  34. #include <FL/Fl_Browser_.H>
  35. #include <FL/Fl_Input.H>
  36. #include <FL/fl_draw.H>
  37. #include <FL/filename.H>
  38.  
  39. #include <stdlib.h>
  40. #include <string.h>
  41. #include <errno.h>
  42. #include <ctype.h>
  43.  
  44. static void default_callback(const char*) {}
  45. static void (*current_callback)(const char*) = default_callback;
  46. void fl_file_chooser_callback(void (*cb)(const char*)) {
  47.   current_callback = cb ? cb : default_callback;
  48. }
  49.  
  50. // "File Chooser Browser" widget:
  51. class FCB : public Fl_Browser_ {
  52.   void* item_first() const ;
  53.   void* item_next(void*) const ;
  54.   void* item_prev(void*) const ;
  55.   int item_height(const dirent*, int) const ;
  56.   int item_height(void*) const ;
  57.   int item_width(const dirent*) const ;
  58.   int item_width(void*) const ;
  59.   int item_quick_height(void*) const ;
  60.   int incr_height() const ;
  61.   void item_draw(void*, int, int, int, int) const ;
  62.   int checkdir(const dirent*, char*) const ;
  63.   void draw();
  64.   void clear_prev();
  65. public:
  66.   char listed[FL_PATH_MAX];// current dir & starname
  67.   int dirend;        // points after last / before starname
  68.   int nameend;        // length to trailing '*' or '\0'
  69.   const char* pattern;    // default pattern
  70.   dirent** list;    // the file names
  71.   dirent** last;    // pointer after end of list
  72.   const char* message;    // message if no file names
  73.   char preved[FL_PATH_MAX];// directory listed in prev
  74.   dirent** prev;    // cached list of another directory
  75.   dirent** prev_last;    // end of that list
  76.   int prev_count;
  77.   FCB(int x, int y, int w, int h) : Fl_Browser_(x, y, w, h, 0) {
  78.     type(FL_HOLD_BROWSER);
  79.     listed[0] = 0;
  80.     dirend = nameend = 1;
  81.     pattern = 0;
  82.     list = prev = 0;
  83.     message = 0;
  84.   }
  85.   // ~FCB nyi
  86.   void clear();
  87.   void set(const char*);
  88.   int get(char*);
  89. };
  90.  
  91. // "File Chooser Window" widget:
  92. class FCW : public Fl_Window {
  93. public:
  94.   int handle(int);
  95.   Fl_Input input;
  96.   Fl_Button* ok_button;
  97.   Fl_Button* cancel_button;
  98.   Fl_Button* normal_button;
  99.   FCB browser;
  100.   FCW();
  101. };
  102.  
  103. /* Files are marked as being directories by replacing the trailing null
  104.    with a '/' if it is a directory, a '\001' if it is *not* a directory.
  105.    An item has height (and is thus selectable) if it is either a directory
  106.    or if it matches the pattern.  Quick-height assummes all unknown files
  107.    are directories, and thus saves the time needed to do a stat().
  108. */
  109.  
  110. // return pointer to last character:
  111. static const char* end_of_name(const dirent* d) {
  112. #if HAVE_DIRENT_H
  113.   const char* e;
  114.   for (e = d->d_name; ;e++) switch (*e) {
  115.   case 0: case 1: case '/': return e;
  116.   }
  117. #else
  118.   // warning: clobbers byte after end of name
  119.   return d->d_name + d->d_namelen;
  120. #endif
  121. }
  122.  
  123. // return true if item is directory, when given pointer to last character:
  124. int FCB::checkdir(const dirent* d, char* e) const {
  125.   if (*e == 1) return 0;
  126.   if (*e == '/') return 1;
  127.   char buf[FL_PATH_MAX];
  128.   memcpy(buf, listed, dirend);
  129.   memcpy(buf+dirend, d->d_name, e-d->d_name);
  130.   *(buf+dirend+(e-d->d_name)) = 0;
  131.   if (filename_isdir(buf)) {
  132.     *e = '/'; return 1;
  133.   } else {
  134.     *e = 1; return 0;
  135.   }
  136. }
  137.  
  138. void* FCB::item_first() const {return list;}
  139.  
  140. void* FCB::item_next(void* p) const {
  141.   if ((dirent**)p+1 >= last) return 0;
  142.   return (dirent**)p+1;
  143. }
  144.  
  145. void* FCB::item_prev(void* p) const {
  146.   if ((dirent**)p <= list) return 0;
  147.   return ((dirent**)p)-1;
  148. }
  149.  
  150. static int ido_matching(const dirent* p, const char* e, const char* n) {
  151.   // replace / or 1 at end with 0 and do match, then put back.  yukko
  152.   int save = *e; *(char*)e = 0;
  153.   int r = filename_match(p->d_name, n);
  154.   *(char*)e = save;
  155.   return(r);
  156. }
  157.  
  158. int FCB::incr_height() const {return textsize()+2;}
  159.  
  160. int FCB::item_height(const dirent* p, int slow) const {
  161.   const char* e = end_of_name(p);
  162.   if (listed[dirend]) {
  163. //  if (p->d_name[0]=='.' && listed[dirend]!='.') return 0;
  164.     if (listed[nameend-1]=='/') {
  165.       if (slow ? !checkdir(p, (char*)e) : *e==1) return 0;
  166.       ((char*)listed)[nameend-1] = 0;
  167.       int r = ido_matching(p, e, listed+dirend);
  168.       ((char*)listed)[nameend-1] = '/';
  169.       if (!r) return 0;
  170.     } else {
  171.       if (!ido_matching(p, e, listed+dirend)) return 0;
  172.     }
  173.   } else {
  174.     if (p->d_name[0]=='.') return 0;
  175.     if (pattern && (slow ? !checkdir(p, (char*)e) : *e==1) &&
  176.     !ido_matching(p, e, pattern)) return 0;
  177.   }
  178.   return textsize()+2;
  179. }
  180.  
  181. int FCB::item_height(void* x) const {
  182.   return item_height(*(const dirent**)x, 1);
  183. }
  184.  
  185. int FCB::item_quick_height(void* x) const {
  186.   return item_height(*(const dirent**)x, 0);
  187. }
  188.  
  189. void FCB::item_draw(void* v, int x, int y, int, int h) const {
  190.   const dirent* p = *(const dirent**)v;
  191.   const char* e = end_of_name(p);
  192.   if (checkdir(p, (char*)e)) e++;
  193.   if (v == selection()) fl_color(contrast(textcolor(), selection_color()));
  194.   else fl_color(textcolor());
  195.   fl_font(textfont(), textsize());
  196.   fl_draw(p->d_name, e-p->d_name, x+4, y+h-3);
  197. }
  198.  
  199. int FCB::item_width(const dirent* p) const {
  200.   const char* e = end_of_name(p); if (*e == '/') e++;
  201.   fl_font(textfont(), textsize());
  202.   return (int)fl_width(p->d_name, e-p->d_name)+4;
  203. }
  204.  
  205. int FCB::item_width(void* x) const {
  206.   return item_width(*(const dirent**)x);
  207. }
  208.  
  209. // "get" the current value by copying the name of the selected file
  210. // or if none are selected, by copying as many common letters as
  211. // possible of the matched file list:
  212. int FCB::get(char* buf) {
  213.   dirent** q = (dirent**)selection(); // the file to copy from
  214.   int n = 0;    // number of letters
  215.   if (q) {    // a file is selected
  216.     const char* e = end_of_name(*q);
  217.     n = e - (*q)->d_name;
  218.     if (*e == '/') n++;
  219.   } else {    // do filename completion
  220.     for (q = list; q < last && !item_height(*q, 0); q++);
  221.     if (q < last) {
  222.       const char* e = end_of_name(*q);
  223.       n = e - (*q)->d_name;
  224.       if (*e == '/') n++;
  225.       for (dirent** r = q+1; n && r < last; r++) {
  226.     if (!item_height(*r, 0)) continue;
  227.     int i;
  228.     for (i=0; i<n && (*q)->d_name[i]==(*r)->d_name[i]; i++);
  229.     n = i;
  230.       }
  231.     }
  232.   }
  233.   if (n) {
  234.     memcpy(buf, listed, dirend);
  235.     memcpy(buf+dirend, (*q)->d_name, n);
  236.     buf[dirend+n]=0;
  237.   }
  238.   return n;
  239. }
  240.  
  241. // "set" the current value by changing the directory being listed and
  242. // changing the highlighted item, if possible:
  243. void FCB::set(const char* buf) {
  244.  
  245.   int bufdirend;
  246.   int ispattern = 0;
  247.   const char* c = buf;
  248.   for (bufdirend=0; *c;) switch(*c++) {
  249.   case '?': case '[': case '*': case '{': ispattern = 1; goto BREAK;
  250. #if defined(WIN32) || defined(__EMX__)
  251.   case '\\':
  252. #endif
  253.   case '/': bufdirend=c-buf; break;
  254.   }
  255. #if defined(WIN32) || defined(__EMX__)
  256.   if ((!bufdirend) && isalpha(buf[0]) && (buf[1]==':')) bufdirend = 2;
  257. #endif
  258.  BREAK:
  259.   int bufend = strlen(buf);
  260.   if (bufend<=bufdirend) ispattern = 1;
  261.  
  262.   // if directory is different, change list to xxx/ :
  263.   if (bufdirend != dirend || strncmp(buf, listed, bufdirend)) {
  264.     if (prev &&
  265.     preved[bufdirend]==0 && !strncmp(buf, preved, bufdirend)) {
  266.       strcpy(preved, listed); preved[dirend] = 0;
  267.       dirent** t;
  268.       t = prev; prev = list; list = t;
  269.       t = prev_last; prev_last = last; last = t;
  270.       strcpy(listed, buf);
  271.       dirend = nameend = bufdirend;
  272.       message = 0;
  273.     } else {
  274.       if (list) {
  275.     clear_prev();
  276.     strcpy(preved, listed); preved[dirend]=0;
  277.     prev = list;
  278.     prev_last = last;
  279.       }
  280.       list = last = 0;
  281.       message = "reading..."; redraw(); Fl::flush(); redraw();
  282.       strcpy(listed, buf);
  283.       dirend = nameend = bufdirend;
  284.       listed[dirend] = listed[dirend+1] = 0;
  285.       int n = filename_list(dirend ? listed : ".", &list);
  286.       if (n < 0) {
  287.     if (errno==ENOENT) message = "No such directory";
  288.     else message = strerror(errno);
  289.     n = 0; list = 0;
  290.       } else message = 0;
  291.       last = list+n;
  292.     }
  293.     if (list && last <= list+2) message = "Empty directory";
  294.     new_list();
  295.   }
  296.  
  297.   dirent** q = 0; // will point to future selection
  298.   int any = 0; // true if any names shown
  299.  
  300.   // do we match one item in the previous list?
  301.   if (!ispattern && bufend >= nameend) {
  302.     for (q = list; ; q++) {
  303.       if (q >= last) {q = 0; break;}
  304.       if (item_height(*q, 0)==0) continue;
  305.       any = 1;
  306.       const char* a = (*q)->d_name;
  307.       const char* b = buf+bufdirend;
  308. #ifdef WIN32
  309.       while (*b && tolower(*a)==tolower(*b)) {a++; b++;}
  310. #else
  311.       while (*b && *a==*b) {a++; b++;}
  312. #endif
  313.       if (!*b && (*a==0 || /* *a=='/' ||*/ *a==1)) break;
  314.     }
  315.   }
  316.  
  317.   // no, change the list pattern to the new text + a star:
  318.   if (!q) {
  319.     strcpy(listed+dirend, buf+bufdirend);
  320.     nameend = bufend;
  321.     if (!ispattern) {listed[nameend]='*'; listed[nameend+1]=0;}
  322.     any = 0;
  323.     // search again for an exact match:
  324.     for (q = list; ; q++) {
  325.       if (q >= last) {q = 0; break;}
  326.       if (item_height(*q, 0)==0) continue;
  327.       any = 1;
  328.       const char* a = (*q)->d_name;
  329.       const char* b = buf+bufdirend;
  330. #ifdef WIN32
  331.       while (*b && tolower(*a)==tolower(*b)) {a++; b++;}
  332. #else
  333.       while (*b && *a==*b) {a++; b++;}
  334. #endif
  335.       if (!*b && (*a==0 || /* *a=='/' ||*/ *a==1)) break;
  336.     }
  337.     new_list();
  338.   }
  339.  
  340.   if (any) message = 0;
  341.   else if (!message) message = "No matching files";
  342.   select_only(q);
  343.   if (q) current_callback(buf);
  344. }
  345.  
  346. void FCB::draw() {
  347.   if (!message) {
  348.     Fl_Browser_::draw();
  349.     if (full_height() > 0) return;
  350.     message = "No matching files";
  351.   }
  352.   Fl_Boxtype b = box(); if (!b) b = FL_DOWN_BOX;
  353.   draw_box(b,color());
  354.   fl_color(FL_INACTIVE_COLOR);
  355.   fl_font(textfont(), textsize());
  356.   fl_draw(message, x()+7, y()+3, w(), h()-3, FL_ALIGN_TOP_LEFT);
  357.   // insure scrollbars are redrawn if error message goes away:
  358.   scrollbar.redraw();
  359.   hscrollbar.redraw();
  360. }
  361.  
  362. void FCB::clear_prev() {
  363.   if (prev) {
  364.     for (dirent**p=prev_last-1; p>=prev; p--) free((void*)*p);
  365.     free((void*)prev);
  366.     prev = prev_last = 0;
  367.   }
  368. }
  369.  
  370. void FCB::clear() {
  371.   if (list) {
  372.     for (dirent**p=last-1; p>=list; p--) free((void*)*p);
  373.     free((void*)list);
  374.     list = last = 0;
  375.   }
  376.   clear_prev();
  377.   listed[0] = 0; dirend = 1;
  378. }
  379.  
  380. ////////////////////////////////////////////////////////////////
  381.  
  382. static void fcb_cb(Fl_Widget*, void* v) {
  383.   FCW* w = (FCW*)v;
  384.   char buf[FL_PATH_MAX];
  385.   if (w->browser.get(buf)) {
  386.     w->input.value(buf);
  387.     w->input.position(10000);
  388. //  w->input.position(10000, w->browser.dirend);
  389.     if (Fl::event_button()==1) {
  390.       if (Fl::event_clicks()) w->ok_button->do_callback();
  391.       else w->browser.set(buf);
  392.     } else {
  393.       current_callback(buf);
  394.     }
  395.   }
  396. }
  397.  
  398. static void tab_cb(Fl_Widget*, void* v) {
  399.   FCW* w = (FCW*)v;
  400.   char buf[FL_PATH_MAX];
  401.   if (w->browser.get(buf)) {
  402.     w->input.value(buf);
  403.     w->input.position(10000);
  404.     w->browser.set(buf);
  405.   }
  406. }
  407.  
  408. #if defined(WIN32) || defined(__EMX__)
  409. // ':' needs very special handling!
  410. static inline int isdirsep(char c) {return c=='/' || c=='\\';}
  411. #else
  412. #define isdirsep(c) ((c)=='/')
  413. #endif
  414.  
  415. static void input_cb(Fl_Widget*, void* v) {
  416.   FCW* w = (FCW*)v;
  417.   const char* buf = w->input.value();
  418.   char localbuf[FL_PATH_MAX];
  419.   if (buf[0] && isdirsep(buf[w->input.size()-1])
  420.       && filename_expand(localbuf, buf)) {
  421.     buf = localbuf;
  422.     w->input.value(localbuf);
  423.     w->input.position(10000);
  424.   }
  425.   w->browser.set(buf);
  426. }
  427.  
  428. static void up_cb(Fl_Widget*, void* v) { // the .. button
  429.   FCW* w = (FCW*)v;
  430.   char* p;
  431.   char* newname;
  432.   char buf[FL_PATH_MAX];
  433.   p = w->browser.listed+w->browser.dirend-1; // point right before last '/'
  434.   if (p < w->browser.listed)
  435.     newname = "../"; // go up from current directory
  436.   else {
  437.     for (; p>w->browser.listed; p--) if (isdirsep(*(p-1))) break;
  438.     if (isdirsep(*p) || *p=='.' &&
  439.     (isdirsep(p[1]) || p[1]=='.' && isdirsep(p[2]))) {
  440.       p = w->browser.listed+w->browser.dirend;
  441.       memcpy(buf, w->browser.listed, p-w->browser.listed);
  442.       strcpy(buf+(p-w->browser.listed), "../");
  443.     } else {
  444.       memcpy(buf, w->browser.listed, p-w->browser.listed);
  445.       buf[p-w->browser.listed] = 0;
  446.     }
  447.     newname = buf;
  448.   }
  449.   w->input.value(newname);
  450.   w->input.position(10000);
  451.   w->browser.set(newname);
  452. }
  453.  
  454. static void dir_cb(Fl_Widget* obj, void* v) { // directory buttons
  455.   FCW* w = (FCW*)v;
  456.   const char* p = obj->label(); if (*p=='&') p++;
  457.   char buf[FL_PATH_MAX];
  458.   char* q; for (q=buf; *p && *p!=' '; *q++ = *p++); *q = 0;
  459.   filename_expand(buf, buf);
  460.   w->input.value(buf);
  461.   w->input.position(10000);
  462.   w->browser.set(buf);
  463. }
  464.  
  465. static void working_cb(Fl_Widget*, void* v) { // directory buttons
  466.   FCW*w = (FCW*)v;
  467.   char buf[FL_PATH_MAX];
  468.   filename_absolute(buf, "");
  469.   w->input.value(buf);
  470.   w->input.position(10000);
  471.   w->browser.set(buf);
  472. }
  473.  
  474. static void files_cb(Fl_Widget* obj, void* v) { // file pattern buttons
  475.   FCW* w = (FCW*)v;
  476.   char buf[FL_PATH_MAX];
  477.   strcpy(buf, w->input.value());
  478.   char* q = buf+w->browser.dirend;
  479.   if (obj != w->normal_button) {    // tack on first word of label
  480.     const char* p = obj->label(); if (*p=='&') p++;
  481.     for (; *p && *p!=' '; *q++ = *p++);
  482.   }
  483.   *q = 0;
  484.   w->input.value(buf);
  485.   w->input.position(10000, w->browser.dirend);
  486.   w->browser.set(buf);
  487. }
  488.  
  489. /*----------------------- The Main Routine ----------------------*/
  490. #define HEIGHT_BOX    (4*WIDTH_SPC+HEIGHT_BUT+HEIGHT_INPUT+HEIGHT_BROWSER)
  491. #define HEIGHT_BUT    25
  492. #define HEIGHT_INPUT    30
  493. #define HEIGHT_BROWSER    (9*HEIGHT_BUT+2) // must be > buttons*HEIGHT_BUT
  494. #define WIDTH_BOX    (3*WIDTH_SPC+WIDTH_BUT+WIDTH_BROWSER)
  495. #define WIDTH_BROWSER    350
  496. #define WIDTH_BUT    125
  497. #define WIDTH_OK    70
  498. #define WIDTH_SPC    5
  499.  
  500. int FCW::handle(int event) {
  501.   if (Fl_Window::handle(event)) return 1;
  502.   if (event==FL_KEYBOARD && Fl::event_key()==FL_Tab) {
  503.     tab_cb(this, this);
  504.     return 1;
  505.   }
  506.   return 0;
  507. }
  508.  
  509. // set this to make extra directory-jumping button:
  510. const char* fl_file_chooser_button;
  511. extern const char* fl_ok;
  512. extern const char* fl_cancel;
  513.  
  514. FCW::FCW() : Fl_Window(WIDTH_BOX, HEIGHT_BOX),
  515.     input(WIDTH_SPC, HEIGHT_BOX-HEIGHT_BUT-2*WIDTH_SPC-HEIGHT_INPUT,
  516.           WIDTH_BOX-2*WIDTH_SPC, HEIGHT_INPUT, 0),
  517.     browser(2*WIDTH_SPC+WIDTH_BUT, WIDTH_SPC,
  518.         WIDTH_BROWSER, HEIGHT_BROWSER)
  519. {
  520.   int but_y = WIDTH_SPC;
  521.   input.callback(input_cb, this);
  522.   input.when(FL_WHEN_CHANGED);
  523.   //  add(browser);
  524.   browser.callback(fcb_cb, this);
  525.  
  526.   begin();
  527.   Fl_Widget* obj;
  528.   obj = ok_button = new Fl_Return_Button(
  529.     WIDTH_BOX-2*(WIDTH_SPC+WIDTH_OK), HEIGHT_BOX-WIDTH_SPC-HEIGHT_BUT,
  530.     WIDTH_OK, HEIGHT_BUT, fl_ok);
  531.   obj = cancel_button = new Fl_Button(
  532.     WIDTH_BOX-WIDTH_SPC-WIDTH_OK, HEIGHT_BOX-WIDTH_SPC-HEIGHT_BUT,
  533.     WIDTH_OK, HEIGHT_BUT, fl_cancel);
  534.   cancel_button->shortcut("^[");
  535.  
  536.   obj=new Fl_Button(WIDTH_SPC,but_y,WIDTH_BUT,HEIGHT_BUT, "&Up one directory");
  537.   obj->callback(up_cb, this);
  538.   but_y += HEIGHT_BUT;
  539.  
  540.   obj = new Fl_Button(WIDTH_SPC, but_y, WIDTH_BUT, HEIGHT_BUT, "&~/ Home");
  541.   obj->callback(dir_cb, this);
  542.   but_y += HEIGHT_BUT;
  543.  
  544.   obj = new Fl_Button(WIDTH_SPC, but_y, WIDTH_BUT, HEIGHT_BUT, "&/ Root");
  545.   obj->callback(dir_cb, this);
  546.   but_y += HEIGHT_BUT;
  547.  
  548.   obj=new Fl_Button(WIDTH_SPC, but_y, WIDTH_BUT, HEIGHT_BUT, "&Current dir");
  549.   obj->callback(working_cb, this);
  550.   but_y += HEIGHT_BUT;
  551.  
  552.   if (fl_file_chooser_button) {
  553.     obj=new Fl_Button(WIDTH_SPC,but_y,WIDTH_BUT,HEIGHT_BUT,fl_file_chooser_button);
  554.     obj->callback(dir_cb, this);
  555.     but_y += HEIGHT_BUT;
  556.   }
  557.  
  558.   normal_button = new Fl_Button(WIDTH_SPC, but_y, WIDTH_BUT, HEIGHT_BUT, "");
  559.   normal_button->callback(files_cb, this);
  560.   but_y += HEIGHT_BUT;
  561.  
  562.   obj = new Fl_Button(WIDTH_SPC,but_y, WIDTH_BUT, HEIGHT_BUT, "* &All files");
  563.   obj->callback(files_cb, this);
  564.   but_y += HEIGHT_BUT;
  565.  
  566.   obj = new Fl_Button(WIDTH_SPC,but_y,WIDTH_BUT,HEIGHT_BUT, ". &Hidden files");
  567.   obj->callback(files_cb, this);
  568.   but_y += HEIGHT_BUT;
  569.  
  570.   obj = new Fl_Button(WIDTH_SPC,but_y,WIDTH_BUT,HEIGHT_BUT, "*/ &Directories");
  571.   obj->callback(files_cb, this);
  572.   but_y += HEIGHT_BUT;
  573.  
  574.   resizable(new Fl_Box(browser.x(), but_y,
  575.                cancel_button->x()-browser.x(),
  576.                browser.y()+browser.h()-but_y));
  577.   // add(input); // put last for better draw() speed
  578.   end();
  579.   set_modal();
  580. }
  581.  
  582. char* fl_file_chooser(const char* message, const char* pat, const char* fname)
  583. {
  584.   static FCW* f; if (!f) f = new FCW();
  585.   f->ok_button->label(fl_ok);
  586.   f->cancel_button->label(fl_cancel);
  587.  
  588.   if (pat && !*pat) pat = 0;
  589.   if (fname && *fname) {
  590.     f->input.value(fname);
  591.   } else if (f->browser.pattern != pat && (!pat || !f->browser.pattern ||
  592.                        strcmp(pat,f->browser.pattern))) {
  593.     // if pattern is different, remove name but leave old directory:
  594.     const char* p = f->input.value();
  595.     const char* q = filename_name(p);
  596.     f->input.value(p, q-p);
  597.   }
  598.   f->browser.pattern = pat;
  599.   f->normal_button->label(pat ? pat : "visible files");
  600.   f->browser.set(f->input.value());
  601.   f->input.position(10000, f->browser.dirend);
  602.  
  603.   f->label(message);
  604.   f->hotspot(f);
  605.   f->show();
  606.   int ok = 0;
  607.   for (;;) {
  608.     Fl::wait();
  609.     Fl_Widget* o = Fl::readqueue();
  610.     if (o == f->ok_button) {ok = 1; break;}
  611.     else if (o == f->cancel_button || o == f) break;
  612.   }
  613.   f->hide();
  614.   f->browser.clear();
  615.  
  616.   if (!ok) return 0;
  617.   const char* r = f->input.value();
  618.   const char* p;
  619.   for (p=r+f->browser.dirend; *p; p++)
  620.     if (*p=='*' || *p=='?' || *p=='[' || *p=='{') return 0;
  621.   return (char*)r;
  622. }
  623.  
  624. //
  625. // End of "$Id: fl_file_chooser.cxx,v 1.10 1999/01/07 21:22:28 mike Exp $".
  626. //
  627.